package us.mmapp.services;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;

import us.mmapp.interfaces.PhotoDownloaderListener;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.util.Log;

/**
 * Photo Downloader from URL.
 *
 */
public class PhotoDownloader
{
	private PhotoDownloader() {};
	
	private static HashMap<String, Bitmap> cache;
	private static HashSet<String> pending;
	
	private static long usedSize;
	
	private static final long MAX_CAPACITY = 1000 * 1000 * 25;
	
	private static ArrayList<PhotoDownloaderListener> listeners;
	
	public static void attach(PhotoDownloaderListener listener)
	{
		if (listeners == null)
			listeners = new ArrayList<PhotoDownloaderListener>();
		listeners.add(listener);
	}
	
	public static void deattach(PhotoDownloaderListener listener)
	{
		if (listeners != null && listeners.contains(listener))
			listeners.remove(listener);
	}
	
	/**
	 * Announce the success.
	 * 
	 * @param reqId the id of the request that was succesful.
	 */
	private static void notifyAllListeners(String location, Bitmap photo)
	{
		Log.v("DOWNLOAD", "SUCCESS ON: " + location);
		if (listeners != null)
			for (PhotoDownloaderListener l : listeners)
				l.available(location, photo);
	}
	
	private synchronized static void addToCache(String location, Bitmap photo)
	{
		if (cache == null)
		{
			cache = new HashMap<String, Bitmap>();
			usedSize = 0;
		}
		
		if (photo == null)
		{
			cache.put(location, null);
		}
		else
		{
			long newSize = photo.getHeight() * photo.getWidth();
			// free up the cache
			for (String key : cache.keySet())
			{
				if (usedSize + newSize < MAX_CAPACITY)
					break;
				// delete from cache
				Bitmap toDelete = cache.get(key);
				if (toDelete != null)
					usedSize -= toDelete.getHeight() * toDelete.getWidth();
				cache.remove(key);
			}
			
			cache.put(location, photo);
			if (photo != null)
				usedSize += newSize;
		}
	}
	
	public synchronized static Bitmap getPhoto(String location)
	{
		if (cache != null && cache.containsKey(location))
			return cache.get(location);
		else
		{
			download(location);
			return null;
		}
	}
	private static final int REQ_CHECK  = 1;
	private static final int REQ_ADD    = 2;
	private static final int REQ_REMOVE = 3;
	
	/**
	 * Access the pending set with a request.
	 * 
	 * @param req
	 * @param location
	 * @return if its contained or not
	 */
	private static synchronized boolean accessPending(int req, String location)
	{
		boolean ret = false;
		if (pending == null)
		{
			pending = new HashSet<String>();
		}
		switch (req)
		{
			case REQ_CHECK:
			{
				ret = pending.contains(location);
				break;
			}
			case REQ_ADD:
			{
				pending.add(location);
				ret = false;
				break;
			}
			case REQ_REMOVE:
			{
				pending.remove(location);
				ret = false;
				break;
			}
		}
		return ret;
	}
	/**
	 * 
	 * @param location - url
	 */
	public static void download(String location)
	{
		// Check if already requested for
		if (accessPending(REQ_CHECK, location))
			return;
		
		Log.v("DOWNLOAD", "REQUEST FOR: " + location);
		accessPending(REQ_ADD, location);
		AsyncTask<String, Void, Object[]> t = new AsyncTask<String, Void, Object[]>()
		{
			@Override
			protected Object[] doInBackground(String... params)
			{
				URLConnection urlConn;
				URL url;
				Object[] ret = {params[0], null};
				try
				{
					url = new URL(params[0]);
				} catch (MalformedURLException e)
				{
					return ret;
				}
				try
				{
					urlConn = url.openConnection();
				} catch (IOException e)
				{
					return ret;
				}
				
				try
				{
					ret[1] = BitmapFactory.decodeStream(urlConn.getInputStream());
					return ret;
				} catch (IOException e)
				{
					return ret;
				}
			}
			@Override
			protected void onPostExecute(Object[] result)
			{
				addToCache((String) result[0], (Bitmap) result[1]);
				accessPending(REQ_REMOVE, (String) result[0]);
				notifyAllListeners((String) result[0], (Bitmap) result[1]);
			}
		};
		t.execute(location);
	}
}
